package server;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;



/**
 * 
 * 前方注意,向web发送的数据，必须滤掉其域名部分，切记切记
 * 
 * 
 * */
public class MSocket implements Runnable {

	private Socket socket;// 套接字

	private Socket webSocket;
	
	private HashMap<String,Socket> webConnections=new HashMap<String,Socket>();
	
	private StringBuilder sb = new StringBuilder();
	
	
	private HTTPRequest request ;
	
	private HTTPResponse response ;

	/**
	 * 请求的数据相当于把一个请求的所有byte数据都装在这个里面
	 * */
	private ByteArrayOutputStream requestData = new ByteArrayOutputStream();

	/**
	 * 响应的数据，把读取到的所有响应数据都装在其中
	 * */
	private ByteArrayOutputStream responseData = new ByteArrayOutputStream();

	public MSocket(Socket socket) throws IOException {
		this.socket = socket;
	}

	@Override
	public void run() {
		while (true) {
			try {
				// ------清空缓存------------/
				requestData.reset();
				responseData.reset();
				// ------------------
				request = new HTTPRequest();
				response = new HTTPResponse();
				//
				//读取请求行，并且修改URI的内容
				String requestLine = readLine(socket.getInputStream());
				String[] temp = requestLine.split(" ");
				request.setMethod(temp[0]);
				request.setUri(temp[1]);
				request.setVersion(temp[2]);
				
				//读取首部行
				while (true) {
					String header = readLine(socket.getInputStream(), requestData);
					System.out.println(header);
					if (header.equals("")) {
						break;
					} else {
						temp = header.split(":");
						request.setHeader(temp[0].trim(), temp[1].trim());
					}
				}
							
				//判断是否为SSL
				if(request.getMethod().equals("CONNECT")){
					// 建立连接
					try{
						System.out.println("---------------------SSL");
						System.out.println(requestLine);
						String[] address=request.getUri().split(":");
						String host=address[0];
						int port=80;
						if(address.length>1){
							port=Integer.valueOf(address[1]);
						}
						InetAddress ine=InetAddress.getByName(host);
						Socket SSLConnection=new Socket(ine, port);
						// 响应客户端(如果建立成功则向服务器返回信息)
						// ------------------------------------//
						// 200响应//尚未实现
						socket.getOutputStream().write("HTTP/1.0 200 Connection established\r\n\r\n".getBytes("ASCII"));	
						System.out.println("SSL OK");
						// --------------------------------------//
						// 双向隧道交换数据
						new SSLTunnel(socket, SSLConnection);
					}catch(Exception e){
						e.printStackTrace();
						socket.getOutputStream().write("HTTP/1.1 404 NOTFOUND\r\n".getBytes("ASCII"));
						close();
					}
					break;
				}else{				
					URL url = new URL(request.getUri());
					int port = url.getPort() == -1 ? 80 : url.getPort();
					InetAddress ine=InetAddress.getByName(url.getHost());
					if(webConnections.get(url.getHost()+port)==null){
						Socket soc=new Socket(ine,port);
						webConnections.put(url.getHost()+port, soc);
					}else{
						Socket s=webConnections.get(url.getHost()+port);
						if(s.isClosed()){
							s.close();
							Socket soc=new Socket(ine, port);
							webConnections.put(url.getHost()+port, soc);
						}
					}
					
					System.out.println(url.getFile());					
					webSocket=webConnections.get(url.getHost()+port);					
					webSocket.getOutputStream().write((request.getMethod()+" ").getBytes("ASCII"));
					webSocket.getOutputStream().write((url.getFile()+" ").getBytes("ASCII"));
					webSocket.getOutputStream().write((request.getVersion()+"\r\n").getBytes("ASCII"));
					
					if (!request.getMethod().equals("HEAD")) {// HEAD 为不带请求内容的请求
						// 读取实体部分
						// 采用分块传输
						if ("chunked".equals(request
								.getHeader("Transfer-Encoding"))) {
							// 读取chunk
							while (true) {
								String sizeLine = readLine(socket.getInputStream(), requestData);
								// 将16进制字符串转成10进制的数字
								int size = castHexStringToLong(sizeLine);
								if (size == 0) {
									readLine(socket.getInputStream(), requestData);
									break;
								}
								readBytes(socket.getInputStream(),size, requestData);
								// 过滤掉最后的\r\n
								readLine(socket.getInputStream(), requestData);
							}
							
						} else {// 读取正常的长度
							if (request.getHeader("Content-Length") != null) {
								int length = Integer.valueOf(request
										.getHeader("Content-Length"));
								readBytes(socket.getInputStream(), length, requestData);
							} else {
								// 判断其是否是多部分的表格提交之类的,尚未确认是否出现该情况时，是否会存在Content-Length
								// 不处理
							}
						}
					}				
					// 向服务器写入数据
					requestData.writeTo(webSocket.getOutputStream());
					// 等待数据返回
					InputStream webin = webSocket.getInputStream();
					
					// 暂不处理超时问题
					// 返回行
					String responseLine = readLine(webin, responseData);
					System.out.println(responseLine);
					String[] responseTemp = responseLine.split(" ");
					response.setVersion(responseTemp[0]);
					response.setStatusCode(responseTemp[1]);
					response.setReasonPhrase(responseTemp[2]);
					// 读取首部字段
					while (true) {
						// 读取返回的首部行
						String headertemp = readLine(webin, responseData);
						System.out.println(headertemp);
						if (headertemp.equals("")) {
							break;
						}
						responseTemp = headertemp.split(":");
						response.setHeader(responseTemp[0].trim(), responseTemp[1].trim());
					}
					// 读取实体部分

					// 先判断本次的请求是否是一个带实体的请求
					if (!request.getMethod().equals("HEAD")) {
						// 是否采用chunked传输
						if ("chunked".equals(response.getHeader("Transfer-Encoding"))) {
							while (true) {
								String sizeLine = readLine(webin, responseData);
								// 将16进制字符串转成10进制的数字
								int size = castHexStringToLong(sizeLine);
								System.out.print(sizeLine+"   "+size);
								if (size == 0) {
									//结束符号是0\r\n\r\n
									if (response.getHeader("Trailer") != null) {
										readLine(webin, responseData);
										System.err.println("Trailer");
									}else{
										readLine(webin, responseData);
									}									
									break;
								}else{
									readBytes(webin,size, responseData);
									// 过滤掉最后的\r\n
									readLine(webin, responseData);
									System.out.println("  ok");
								}
							}
						} else {
							// 判断是否存在content-length
							if (response.getHeader("Content-Length") != null) {
								int length = Integer.valueOf(response
										.getHeader("Content-Length").trim());
								// 读取数据
								if(length>0){
									readBytes(webin, length, responseData);
								}
							} else {
								// 如果不带content-Length,则查看是否是传输了一个复合类型的文件
								if (response.getHeader("Content-Type") != null
										&& response.getHeader("Content-Type")
												.startsWith("multipart")) {
									System.out.println("不支持的类型");
								}
							}
						}
					}
					//数据读取完毕考虑是否可以关闭
					if(response.getHeader("Connection")!=null&&response.getHeader("Connection").equals("close")){
						webSocket.close();
						webConnections.remove(url.getHost()+port);
					}
					//把数据写出去了
					responseData.writeTo(socket.getOutputStream());
					responseData.flush();
				
				}																				
			} catch (Exception e) {
				System.out.println("error in "+request.getUri());
				e.printStackTrace();
				close();
				break;
			}
		}
	}
	
	
	public String readLine(InputStream in) throws Exception{

		byte pre = 0;
		byte read;
		//清空数据缓存区
		sb.delete(0, sb.length());			
		//循环读入数据直到遇到\n
		while (true) {
			// 读入
			int i=in.read();
			if(i==-1){
				throw new Exception();
			}
			read = (byte)i;
			sb.append((char) read);
			if (read == '\n' && pre == '\r') {
				break;
			} else {
				pre = read;
			}
		}
		int length = sb.length();
		// 移除\r\n的数据
		sb.delete(length - 2, length);
		// 返回已经读取到的数据
		return sb.toString();
	}

	/**
	 * 从指定的输入流中读取一行数据，并且写入指定的输出流中
	 * */
	public String readLine(InputStream in, OutputStream out) throws Exception {
			byte pre = 0;
			byte read;
			//清空数据缓存区
			sb.delete(0, sb.length());			
			//循环读入数据直到遇到\n
			while (true) {
				// 读入
				int i=in.read();
				if(i==-1){
					throw new Exception();
				}
				read = (byte)i;
				// 写出
				out.write(read);
				sb.append((char) read);
				if (read == '\n' && pre == '\r') {
					break;
				} else {
					pre = read;
				}
			}
			int length = sb.length();
			// 移除\r\n的数据
			sb.delete(length - 2, length);
			// 返回已经读取到的数据
			return sb.toString();
	}

	public int castHexStringToLong(String hexString) {
		int l = 0;
		hexString = hexString.toUpperCase();
		String array = "0123456789ABCDEF";
		sb.delete(0, sb.length());
		sb.append(hexString);
		sb = sb.reverse();
		int Weights = 16;
		for (int i = 0; i < sb.length(); i++) {
			char s = sb.charAt(i);
			int index = array.indexOf(s);
			l += (Math.pow(Weights, i) * index);
		}
		return l;
	}

	/**
	 * 从指定的输入流中读取指定长度的bytes数组，并且写入指定的输入流中 出现异常则不会向输出流中写入任何数据
	 * */
	public byte[] readBytes(InputStream in, int length, OutputStream out)
			throws Exception {
		if(length>0){
			byte[] bytes = new byte[length];
			int hasread = 0;
			while (hasread < length) {
				int count = in.read(bytes, hasread,length - hasread);
				if (count > 0) {
					hasread += count;
				} else {
					throw new IOException();
				}
			}
			//将读取到的数据写入到指定输出流中
			out.write(bytes);
			return bytes;
		}else{
			return null;
		}
	}

	/**
	 * 关闭连接与对外的所有连接
	 * */
	public void close() {
		try {
		for (Iterator<String> iterator = this.webConnections.keySet().iterator(); iterator.hasNext();) {
			String type = (String) iterator.next();
			this.webConnections.get(type).close();			
			}
		this.webConnections.clear();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{		
			try {
				this.socket.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
